package sisop;

import java.util.LinkedList;
import java.lang.Math;
import java.io.*;

/**
 * Implementacion de un planificador Multilevel Feedback Queue. 
 * Esta clase esta vacia.
 */ 
class MFQ2 extends Scheduler {

	static private int quanta[] = {5,15,45};

	static private int none			= -1;
	static private int high			= 0;
	static private int middle		= 1;
	static private int low			= 2;

	static private int max_io_time	= 5;

	private int finished_tasks;
	private int current_time;
	private Task current_task;
	private int current_priority;
	private int round_time;
	private int switch_time;
	private int time_to_io;

	private LinkedList<LinkedList<Task>> queues;
	
	public MFQ2() {
		super("MFQ2");
	}

	protected void scheduler_init() {
	
		queues = new LinkedList<LinkedList<Task>>();
		queues.add(0,new LinkedList<Task>());
		queues.add(1,new LinkedList<Task>());
		queues.add(2,new LinkedList<Task>());

		finished_tasks=0;
		current_time=-1;
		current_task=null;
		round_time=0;
		switch_time=2;
		time_to_io=-1;
	}


	public String scheduler_next() {
		current_time++;

		SelectReadyTasks();
		ComputeIoTime();

		if (switch_time<2)
		{
			switch_time++;
			return IDLE_TASK;
		}
		else
		{
			if (current_task!=null && (current_task.ttime>=current_task.ptime || round_time>=quanta[current_priority]))
				return StopCurrentTask();
			
			else
			{
				if (current_task==null)
				{
					for (int i=0;i<low+1;i++)
					{
						for (Task task: queues.get(i))
						{
							if (task.remaining_io_time==0)
							{
								current_priority = i;
								current_task = task;
								current_task.wtime += (current_task.ftime<0 ? current_time-current_task.rtime :current_time-current_task.ftime);
								round_time=0;

								boolean usa_io = ((int)(Math.random()*2.0)!=0);
								
								if (usa_io)
								{
									int met = Math.min(current_task.ptime-current_task.ttime,quanta[current_priority]);
									time_to_io = (int)(Math.random()*(met));
									System.out.println(String.format("proceso %s usa io: %b y se va a ejecutar %d ticks", current_task.name,usa_io,time_to_io));
								}
								
								break;
							}
						}
					}
					
					if (current_task==null)
						return IDLE_TASK;
				}

				if (time_to_io==0)
					return StopCurrentTask();

				current_task.ttime++;
				round_time++;

				if (time_to_io>0)
					time_to_io--;
			}
		}

		return current_task.name;
	}

	private String StopCurrentTask()
	{
		queues.get(current_priority).remove();
		current_task.ftime=current_time;

		if (current_task.ttime<current_task.ptime)
		{
			if (time_to_io==0)
			{
				time_to_io=-1;
				current_task.remaining_io_time = 1 + (int)(Math.random()*max_io_time);
				current_task.ftime = current_time + current_task.remaining_io_time;

				int new_priority = current_priority>high ? current_priority-1 : current_priority;
				
				queues.get(new_priority).addLast(current_task);
			}
			else
			{
				if (current_priority<low)
					queues.get(current_priority+1).addLast(current_task);
				else
					queues.get(current_priority).addLast(current_task);
			}
		}
		else
		{
			finished_tasks++;
			
			if (task_set.size()==finished_tasks)
				return null;
		}

		switch_time=1;
		current_priority=none;
		current_task=null;
		return IDLE_TASK;
	}

	private void ComputeIoTime()
	{
		for (int i=0;i<low+1;i++)
			for (Task task: queues.get(i))
				if (task.remaining_io_time>0)
					task.remaining_io_time--;
	}

	private void SelectReadyTasks()
	{
		for (Task task: this.task_set)
			if (task.rtime==current_time)
				queues.get(high).add(task);
	}

}
